<style scoped lang="scss"></style>
<template>
    <div class="clock"></div>
</template>

<script setup lang="ts">
import { onMounted } from "vue";
let container: HTMLDivElement;
let canvas: HTMLCanvasElement;
let ctx: CanvasRenderingContext2D;
/* 分析钟表
    包含的元素：
        1.圆形表盘
        2.刻度线段，刻度文字
        3.时针，分针，秒针
    动画绘制：
        每秒需要把所有的元素重新绘制一遍

*/
class Clock {
    // 表盘相关静态数据
    clockOrigin = { x: 0, y: 0 }; //圆盘坐标点
    clockRadius = 300; //半径
    clockLineWidth = 1; //圆盘粗细
    clockArcColor = "rgb(0,0,0)"; //圆盘线粗细

    // 刻度线相关静态数据
    boldScaleLineWidth = 5; //粗线宽度
    boldScaleLineHeight = 10; //粗线高度
    boldScaleLineColor = "black"; //粗线颜色
    thinScaleLineWidth = 3; //细线宽度
    thinScaleLineHeight = 8; //细线高度
    thinScaleLineColor = "grey"; //细线颜色

    // 刻度文字相关静态数据
    scaleTextFont = "bold 20px sans-serif "; //字体
    scaleTextColor = "black"; //文本颜色
    scaleTextAlign: CanvasTextAlign = "center"; //文本水平位置
    scaleTextVertical: CanvasTextBaseline = "middle"; //文本垂直位置
    scaleTextRadius = 270; //文本围绕的半径

    // 时针相关静态数据
    hourWidth = 8;
    hourLength = 240;
    hourColor = "rgb(0,0,20)";
    hourExtraLength = 15; //尾端长度
    hourHeadLengthRatio = 2.5; //时针头长度系数
    hours = 0; //当前时刻小时数

    // 分针相关静态数据
    minuteWidth = 5;
    minuteLength = 280;
    minuteColor = "rgb(150,150,150)";
    minuteExtraLength = 20; //尾端长度
    minutes = 0; //当前时刻分钟数

    // 秒针相关静态数据
    secondWidth = 2;
    secondLength = 320;
    secondColor = "rgb(255,0,0)";
    secondExtraLength = 25; //尾端长度
    seconds = 0; //当前时刻秒数

    // 圆心点颜色
    originColor = "yellow";
    originRadius = 8;

    constructor() {}
    // 开始绘画
    draw() {
        ctx.save();
        // 将原点定位到中间
        ctx.translate(canvas.width / 2, canvas.height / 2);
        this.drawArc();
        this.drawScaleLines();
        this.drawHour();
        this.drawMinute();
        this.drawSecond();
        this.drawOrigin();
        this.drawScaleText();
    }
    // 绘制圆形表盘
    private drawArc() {
        ctx.save();
        ctx.beginPath();
        ctx.arc(
            this.clockOrigin.x,
            this.clockOrigin.y,
            this.clockRadius,
            0,
            Math.PI * 2
        );
        ctx.strokeStyle = this.clockArcColor;
        ctx.lineWidth = this.clockLineWidth;
        ctx.stroke();
        ctx.restore();
    }
    // 绘制刻度线
    private drawScaleLines() {
        // 绘制粗线
        ctx.save();
        for (let index = 0; index < 12; index++) {
            ctx.beginPath();
            ctx.moveTo(0, this.clockOrigin.y - this.clockRadius);
            ctx.lineTo(
                0,
                this.clockOrigin.y - this.clockRadius + this.boldScaleLineHeight
            );
            ctx.lineWidth = this.boldScaleLineWidth;
            ctx.strokeStyle = this.boldScaleLineColor;
            // 下次绘制在上次旋转角度的基础上每次额外旋转30度
            ctx.rotate((Math.PI * 2) / 12);
            ctx.stroke();
        }
        ctx.restore();
        // 绘制细线
        ctx.save();
        for (let index = 0; index < 60; index++) {
            ctx.beginPath();
            ctx.lineWidth = this.thinScaleLineWidth;
            ctx.strokeStyle = this.thinScaleLineColor;
            ctx.moveTo(0, this.clockOrigin.y - this.clockRadius);
            ctx.lineTo(
                0,
                this.clockOrigin.y - this.clockRadius + this.thinScaleLineHeight
            );
            ctx.rotate((Math.PI * 2) / 60);
            // 跳过大刻度线绘画
            let isThin = index % 5 != 0;
            if (isThin) {
                ctx.stroke();
            }
        }
        ctx.restore();
    }
    // 绘制刻度文字
    private drawScaleText() {
        ctx.save();
        ctx.beginPath();
        ctx.font = this.scaleTextFont;
        ctx.fillStyle = this.scaleTextColor;
        ctx.textAlign = this.scaleTextAlign;
        ctx.textBaseline = this.scaleTextVertical;
        for (let index = 0; index < 12; index++) {
            const text = index == 0 ? "12" : index + "";
            ctx.fillText(
                text,
                Math.sin(((Math.PI * 2) / 12) * index) * this.scaleTextRadius,
                -Math.cos(((Math.PI * 2) / 12) * index) * this.scaleTextRadius
            );
        }
        ctx.restore();
    }
    // 绘制时针
    private drawHour() {
        ctx.save();
        // 一圈有12小时，每小时有60分钟，每分钟有60秒，每秒时针旋转2pi/(60*60*12),将24时换成12时
        ctx.rotate(
            (((this.hours % 12) * 60 * 60 + this.minutes * 60 + this.seconds) *
                Math.PI *
                2) /
                (60 * 60 * 12)
        );
        ctx.beginPath();
        // 1.起点x偏移原点左侧一半宽度，高度y为时针长度减去尾端多余部位长度
        ctx.moveTo(-this.hourWidth / 2, this.hourExtraLength);
        ctx.lineTo(
            -this.hourWidth / 2,
            -(this.hourLength - this.hourExtraLength)
        );
        // 2.曲线控制点偏移起点一半的时针宽度，曲线终点高度为时针长度-尾部长度+倍率*时针宽度
        ctx.quadraticCurveTo(
            -this.hourWidth * 1.5,
            -(this.hourLength - this.hourExtraLength),
            0,
            -(
                this.hourLength -
                this.hourExtraLength +
                this.hourWidth * this.hourHeadLengthRatio
            )
        );
        // 画另一半
        ctx.quadraticCurveTo(
            this.hourWidth * 1.5,
            -(this.hourLength - this.hourExtraLength),
            this.hourWidth / 2,
            -(this.hourLength - this.hourExtraLength)
        );
        ctx.lineTo(this.hourWidth / 2, this.hourExtraLength);
        ctx.fillStyle = this.hourColor;
        ctx.fill();
        ctx.restore();
    }
    // 绘制分针
    private drawMinute() {
        ctx.save();
        // 一圈有60分钟，每分钟有60秒，每秒分针旋转2pi/(60*60)
        ctx.rotate(
            (this.minutes * 60 + this.seconds) * ((Math.PI * 2) / (60 * 60))
        );
        ctx.beginPath();
        ctx.moveTo(0, this.minuteExtraLength);
        ctx.lineTo(0, -(this.minuteLength - this.minuteExtraLength));
        ctx.lineWidth = this.minuteWidth;
        ctx.strokeStyle = this.minuteColor;
        ctx.stroke();
        ctx.restore();
    }
    // 绘制秒针
    private drawSecond() {
        ctx.save();
        // 一圈60秒，一秒转动的弧度就是2pi/60
        ctx.rotate(((Math.PI * 2) / 60) * this.seconds);
        ctx.beginPath();
        ctx.moveTo(0, this.secondExtraLength);
        ctx.lineTo(0, -(this.secondLength - this.secondExtraLength));
        ctx.lineWidth = this.secondWidth;
        ctx.strokeStyle = this.secondColor;
        ctx.stroke();
        ctx.restore();
    }
    // 绘制圆心点
    private drawOrigin() {
        ctx.save();
        ctx.beginPath();
        ctx.arc(
            this.clockOrigin.x,
            this.clockOrigin.y,
            this.originRadius,
            0,
            Math.PI * 2
        );
        ctx.fillStyle = this.originColor;
        ctx.fill();
        ctx.restore();
    }
    // 清除画布
    clear() {
        ctx.restore();
        ctx.clearRect(0, 0, canvas.width, canvas.height);
    }
}
// 制作钟表
const drawClock = () => {
    // 创建表盘
    const clock = new Clock();
    // 获取当前时间
    const getTime = () => {
        const now = new Date();
        const seconds = now.getSeconds();
        const minutes = now.getMinutes();
        const hours = now.getHours();
        return {
            seconds,
            minutes,
            hours,
        };
    };
    // 根据时间更新表盘
    const draw = () => {
        clock.clear();
        const { seconds, minutes, hours } = getTime();
        clock.seconds = seconds;
        clock.minutes = minutes;
        clock.hours = hours;
        clock.draw();
        requestAnimationFrame(draw);
    };
    draw();
};
const init = () => {
    // 初始化一个滑板
    container = document.querySelector(".clock") as HTMLDivElement;
    canvas = document.createElement("canvas");
    canvas.width = 800;
    canvas.height = 800;
    container.appendChild(canvas);
    ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
    // 绘制钟表
    drawClock();
};
onMounted(() => {
    init();
});
</script>
